<?php
abstract class Model {
	/**
	 * 数据库对象
	 *
	 * @var object
	 */
	protected $db;
	
	/**
	 * 缓存对象
	 *
	 * @var object
	 */
	protected $cache;
	
	/**
	 * 当前记录的数据
	 *
	 * @var array
	 */
	protected $data = array();
	
	/**
	 * 修改过的字段列表
	 *
	 * @var array
	 */
	protected $changeFields = array();
	
	/**
	 * 模型对应的数据表
	 *
	 * @var string
	 */
	protected $table = '';
	
	/**
	 * 主键字段名 
	 * @var string
	 */
	protected $pk = 'id';
	
	/**
	 * 要进行验证的数据
	 *
	 * @var string
	 */
	protected $validate = array();
	
	/**
	 * 错误消息
	 *
	 * @var string
	 */
	protected $error = '';
	
	/**
	 * 当前记录在数据库是否存在
	 *
	 * @var boolean
	 */
	public $exists = false;
	
	/**
	 * 是否新增的记录
	 */
	public $isNew = false;

	public final function __construct($id = 0, $userid = 0) {
		$id = intval($id);
		$userid = intval($userid);
		$this->db = JBlog::getDb();
		$this->cache = JBlog::getCache();
		$this->setTable();
		if ($id > 0) {
			$this->load($id, $userid);
		}
	}
	
	/**
	 * 设置模型对应的数据表
	 */
	abstract protected function setTable();
	
	/**
	 * 返回当前模型的数据表
	 * 
	 * @return string
	 */
	public static function getTable() {}
	
	/**
	 * 新增一条记录
	 *
	 * @return boolean
	 */
	public function add() {
		if (!$this->exists && count($this->data)) {
			if ($this->validate()) {
				$id = $this->db->add($this->data, $this->table);
				if ($id > 0) {
					$this->id = $id;
					$this->exists = true;
					$this->isNew = true;
					if ($this->cache) $this->cache->set($this->getCacheKey(), $this->data);
					return true;
				}
			}
		}
		return false;
	}
	
	/**
	 * 保存修改
	 *
	 * @return boolean
	 */
	public function save() {
		if ($this->exists && count($this->changeFields)) {
			if ($this->validate()) {
				$update = array();
				foreach ($this->changeFields as $key => $value) {
					$update[$key] = $this->data[$key];
				}
				$this->db->update($update, array('id'=>$this->id), $this->table);
				if ($this->cache) $this->cache->set($this->getCacheKey(), $this->data);
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 删除
	 */
	public function delete() {
		if ($this->exists) {
			$this->db->delete($this->id, $this->table);
			if ($this->cache) $this->cache->rm($this->getCacheKey());
			$this->exists = false;
			return true;
		}
		return false;
	}
	
	/**
	 * 加载数据
	 * 优先从缓存加载，缓存不存在的话，从数据库加载
	 * 
	 * @param int $id
	 * @param int $userid
	 * @return boolean
	 */
	protected function load($id, $userid) {
		if (!$this->cache || !($data = $this->cache->get($this->getCacheKey()))) {
			$wheresql = "id = '{$id}'";
			if ($userid > 0) $wheresql .= " AND userid = '$userid'";
			$sql = "SELECT * FROM {$this->table} WHERE $wheresql";
			$data = $this->db->getRow($sql);
		}
		if ($data) {
			$this->data = $data;
			$this->exists = true;
		} else {
			$this->exists = false;
		}
		return true;
	}
	
	/**
	 * 从参数提供的数据初始化模型
	 * 
	 * @param array $data 数据库记录
	 */
	public function loadFrom($data) {
		$this->data = $data;
		$this->exists = true;
	}
	
	/**
	 * 返回缓存的KEY
	 *
	 * @return string
	 */
	protected function getCacheKey() {
		return $this->table.'_'.intval($this->id).'_'.intval($this->userid);
	}
	
	/**
	 * 数据验证
	 * 新增或修改保存前进行数据完整性验证
	 * 
	 * @return boolean
	 */
	private function validate() {
		foreach ($this->validate as $value) {
			list($field, $vdtype, $errmsg) = $value;
			if (method_exists($this, "_vd_{$vdtype}")) {
				$ret = call_user_func(array($this, "_vd_{$vdtype}"), $this->{$field}, $field);
			} else {
				$ret = Validate::check($vdtype, $this->{$field});
			}
			if (!$ret) {
				$this->error = $errmsg;
				return false;
			}
		}
		return true;
	}
	
	/**
	 * 验证是否唯一
	 * 
	 * @return boolean
	 */
	public function _vd_unique($string, $field) {
		return $this->db->get($string, $this->table, $field);
	}
	
	/**
	 * 返回错误信息
	 *
	 * @return string
	 */
	public function error() {
		return $this->error;
	}
	
	/**
	 * 将对象转化为数组
	 * 
	 * @param array $fields 
	 */
	public function toArray($fields = null) {
		if (is_array($fields)) {
			$ret = array();
			foreach ($fields as $field) {
				$ret[$field] = $this->{$field};
			}
			return $ret;
		}
		return $this->data;
	}
	
	/**
	 * 将对象转化为stdClass 
	 */
	public function toObject() {
		$obj = new stdClass();
		foreach ($this->data as $key => $value) {
			$obj->{$key} = $value;
		}
		return $obj;
	}
	
	/**
	 * 魔术方法 __get
	 * 
	 * @param string $key
	 */
	public function __get($key) {
		return isset($this->data[$key]) ? $this->data[$key] : null;
	}
	
	/**
	 * 魔术方法 __set
	 * 
	 * @param string $key
	 * @param string $value
	 */
	public function __set($key, $value) {
		if (preg_match('/^[a-z0-9\_]+$/i', $key)) {
			if (!isset($this->data[$key]) || $this->data[$key] != $value) {
				$this->changeFields[$key] ++;
			}
			$this->data[$key] = $value;
		}
	}
	
	/**
	 * 魔术方法，返回字段是否存在
	 * 
	 * @param string $key
	 */
	public function __isset($key) {
		return isset($this->data[$key]);
	}
	
	/**
	 * 魔术方法
	 * 
	 * @param string $key
	 */
	public function __unset($key) {
		 unset($this->data[$key]);
	}
}
